﻿//
// (C) Copyright 2009 Irantha Suwandarathna (iranthas@hotmail.com)
// All rights reserved.
//

/* Copyright (c) 2001-2008, The HSQL Development Group
 * All rights reserved.
 *
 * Redistribution and use _in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * Redistributions of source code must retain the above copyright notice, this
 * list of conditions and the following disclaimer.
 *
 * Redistributions _in binary form must reproduce the above copyright notice,
 * this list of conditions and the following disclaimer _in the documentation
 * and/or other materials provided with the distribution.
 *
 * Neither the name of the HSQL Development Group nor the names of its
 * contributors may be used to endorse or promote products derived from this
 * software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG,
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EffiProz.Core.Errors;
using EffiProz.Core.Lib;
using EffiProz.Core.DataTypes;
using EffiProz.Core.Rights;

namespace EffiProz.Core
{
    public class ColumnSchema : ColumnBase, SchemaObject
    {
        public static ColumnSchema[] emptyArray = new ColumnSchema[] { };

        //
        public QNameManager.QName columnName;
        private bool _isPrimaryKey;
        private Expression defaultExpression;
        private Expression generatingExpression;
        private NumberSequence sequence;

        /**
         * Creates a column defined in DDL statement.
         */
        public ColumnSchema(QNameManager.QName name, SqlType type, bool isNullable,
                            bool isPrimaryKey, Expression defaultExpression)
        {

            columnName = name;
            nullability = isNullable ? SchemaObjectNullability.NULLABLE
                                     : SchemaObjectNullability.NO_NULLS;
            this.dataType = type;
            this._isPrimaryKey = isPrimaryKey;
            this.defaultExpression = defaultExpression;
        }

        public virtual int getType()
        {
            return columnName.type;
        }

        public virtual QNameManager.QName getName()
        {
            return columnName;
        }

        public override String getNameString()
        {
            return columnName.name;
        }

        public override String getOriginalNameString()
        {
            return columnName.originalName;
        }

        public override String getTableNameString()
        {
            return columnName.parent == null ? null
                                             : columnName.parent.name;
        }

        public override String getOriginalTableNameString()
        {
            return columnName.parent == null ? null
                                             : columnName.parent.originalName;
        }

        public virtual QNameManager.QName getSchemaName()
        {
            return columnName.schema;
        }

        public override String getSchemaNameString()
        {
            return columnName.schema == null ? null
                                             : columnName.schema.name;
        }

        public override String getOriginalSchemaNameString()
        {
            return columnName.schema == null ? null
                                             : columnName.schema.originalName;
        }

        public virtual QNameManager.QName getCatalogName()
        {
            return columnName.schema == null ? null
                                             : columnName.schema.schema;
        }

        public override String getCatalogNameString()
        {

            return columnName.schema == null ? null
                                             : columnName.schema.schema == null
                                               ? null
                                               : columnName.schema.schema.name;
        }

        public override String getOriginalCatalogNameString()
        {

            return columnName.schema == null ? null
                                             : columnName.schema.schema == null
                                               ? null
                                               : columnName.schema.schema.originalName;
        }

        public virtual Grantee getOwner()
        {
            return columnName.schema == null ? null
                                             : columnName.schema.owner;
        }

        public virtual OrderedHashSet getReferences()
        {
            return new OrderedHashSet();
        }

        public virtual OrderedHashSet getComponents()
        {
            return null;
        }

        public virtual void compile(Session session, SchemaObject table)
        {

            if (generatingExpression == null)
            {
                return;
            }

            Expression.resolveGenerationExpression(session, (Table)table,
                                                   generatingExpression);

            if (dataType.typeComparisonGroup
                    != generatingExpression.getDataType().typeComparisonGroup)
            {
                throw Error.error(ErrorCode.X_42561);
            }
        }

        public virtual String getSQL()
        {

            StringBuilder sb = new StringBuilder();

            switch (parameterMode)
            {

                case SchemaObjectParameterModes.PARAM_IN:
                    sb.Append(Tokens.T_IN).Append(' ');
                    break;

                case SchemaObjectParameterModes.PARAM_OUT:
                    sb.Append(Tokens.T_OUT).Append(' ');
                    break;

                case SchemaObjectParameterModes.PARAM_INOUT:
                    sb.Append(Tokens.T_INOUT).Append(' ');
                    break;
            }

            if (columnName != null)
            {
                sb.Append(columnName.statementName);
                sb.Append(' ');
            }

            sb.Append(dataType.getTypeDefinition());

            return sb.ToString();
        }

        public override void setType(SqlType type)
        {
            this.dataType = type;
        }

        public void setName(QNameManager.QName name)
        {
            this.columnName = name;
        }

        public void setIdentity(NumberSequence sequence)
        {
            this.sequence = sequence;
            _isIdentity = sequence != null;
        }

        public void setType(ColumnSchema other)
        {
            nullability = other.nullability;
            dataType = other.dataType;
        }

        public NumberSequence getIdentitySequence()
        {
            return sequence;
        }

        /**
         *  Is column nullable.
         *
         * @return bool
         */
        public override bool isNullable()
        {

            bool isNullable = base.isNullable();

            if (isNullable)
            {
                if (dataType.isDomainType())
                {
                    return dataType.userTypeModifier.isNullable();
                }
            }

            return isNullable;
        }

        public override byte getNullability()
        {
            return _isPrimaryKey ? SchemaObjectNullability.NO_NULLS
                                : base.getNullability();
        }

        public bool isGenerated()
        {
            return generatingExpression != null;
        }

        public bool hasDefault()
        {
            return getDefaultExpression() != null;
        }

        /**
         * Is column writeable or always generated
         *
         * @return bool
         */
        public override bool isWriteable()
        {
            return !isGenerated();
        }

        public override void setWriteable(bool value)
        {
            throw Error.runtimeError(ErrorCode.U_S0500, "ColumnSchema");
        }

        public override bool isSearchable()
        {
            return Types.isSearchable(dataType.typeCode);
        }

        /**
         *  Is this single column primary key of the table.
         *
         * @return bool
         */
        public bool isPrimaryKey()
        {
            return _isPrimaryKey;
        }

        /**
         *  Set primary key.
         *
         */
        public void setPrimaryKey(bool value)
        {
            _isPrimaryKey = value;
        }

        /**
         *  Returns default value in the session context.
         */
        public Object getDefaultValue(Session session)
        {

            return defaultExpression == null ? null
                                             : defaultExpression.getValue(session,
                                             dataType);
        }

        /**
         *  Returns generated value in the session context.
         */
        public Object getGeneratedValue(Session session)
        {

            return generatingExpression == null ? null
                                                : generatingExpression.getValue(
                                                session, dataType);
        }

        /**
         *  Returns SQL for default value.
         */
        public String getDefaultSQL()
        {

            String ddl = null;

            ddl = defaultExpression == null ? null
                                            : defaultExpression.getSQL();

            return ddl;
        }

        /**
         *  Returns default expression for the column.
         */
        public Expression getDefaultExpression()
        {

            if (defaultExpression == null)
            {
                if (dataType.isDomainType())
                {
                    return dataType.userTypeModifier.getDefaultClause();
                }

                return null;
            }
            else
            {
                return defaultExpression;
            }
        }

       public void setDefaultExpression(Expression expr)
        {
            defaultExpression = expr;
        }

        /**
         *  Returns generated expression for the column.
         */
       public Expression getGeneratingExpression()
        {
            return generatingExpression;
        }

       public void setGeneratingExpression(Expression expr)
        {
            generatingExpression = expr;
        }

        public ColumnSchema duplicate()
        {

            ColumnSchema copy = new ColumnSchema(columnName, dataType,
                                                 isNullable(), _isPrimaryKey,
                                                 defaultExpression);

            copy.setGeneratingExpression(generatingExpression);
            copy.setIdentity(sequence);

            return copy;
        }

    }
}
